﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using MultiGed2.Provoz;
using MultiGed2.Topologie;

namespace MultiGed2
{
 /// <summary>
 /// Omezeni jizdy vlaku.
 /// </summary>
 public enum OmezeniJizdy
 {
  /// <summary>
  /// Vlak jede denne.
  /// </summary>
  Denne,
  /// <summary>
  /// Vlak jede v (1).
  /// </summary>
  Jede1,
  /// <summary>
  /// Vlak jede v (2).
  /// </summary>
  Jede2,
  /// <summary>
  /// Vlak jede v (3).
  /// </summary>
  Jede3,
  /// <summary>
  /// Vlak jede v (4).
  /// </summary>
  Jede4,
  /// <summary>
  /// Vlak jede v (5).
  /// </summary>
  Jede5,
  /// <summary>
  /// Vlak jede v (6).
  /// </summary>
  Jede6,
  /// <summary>
  /// Vlak jede v (7).
  /// </summary>
  Jede7,
  /// <summary>
  /// Vlak jede v prac. dny.
  /// </summary>
  PracDny,
  /// <summary>
  /// Vlak jede v (6) a (7)
  /// </summary>
  Vikendy,
  /// <summary>
  /// Vlak jede v (1)-(6).
  /// </summary>
  MimoNedele,
  /// <summary>
  /// Vlak jede v (1)-(5) a (7).
  /// </summary>
  MimoSoboty,
  /// <summary>
  /// Vlak jede v (1), (3) a (5)
  /// </summary>
  PoStPa,
  /// <summary>
  /// Vlak jede v (2), (4) a (6)
  /// </summary>
  UtCtSo,
  /// <summary>
  /// Podle potreby.
  /// </summary>
  PodlePotreby
 }

 /// <summary>
 /// Mimoradnosti/operace u vlaku.
 /// </summary>
 public enum MimoradnostVlaku
 {
  /// <summary>
  /// Bez mimoradnosti.
  /// </summary>
  Zadna,
  /// <summary>
  /// Vlak do oblasti vstupuje s postrkem.
  /// </summary>
  JedeSPostrkem,
  /// <summary>
  /// Vlak vykonava NTP, stanice NTP je urcena polozkou dopravnaMimoradnost.
  /// </summary>
  NTP,
  /// <summary>
  /// Vlak vyzaduje postrk ve stanici, urcene polozkou dopravnaMimoradnost.
  /// </summary>
  Postrk,
  /// <summary>
  /// Vlak muze nahodne vyzadovat postrk.
  /// </summary>
  NahodnyPostrk,
  /// <summary>
  /// Vlak nahodne veze mimoradnou zasilku.
  /// </summary>
  Mimoza,
  /// <summary>
  /// Naklapeci souprava.
  /// </summary>
  Pendular
 }

 /// <summary>
 /// Kategorie vlaku.
 /// </summary>
 public enum KategorieVlaku
 {
  Nevybrana,
  EN, EC, IC, Ex, R, Sp, Os, Sv,
  Nex, Rn, Vn, Pn, Mn, Pv, Lv,
  MEN, MEC, MIC, MEx, MR, MSp, MOs, MSv, Osz, MOsz
 }

 /// <summary>
 /// Vycet rychlosti vlaku v km/h. Hodnoty vyctu
 /// maji prirazeny prislusne hodnoty rychlosti v m/s.
 /// </summary>
 public enum Rychlost
 {
  Nevybrana,
  V30 = 8, V40 = 11, V50 = 14,
  V60 = 17, V70 = 19, V80 = 22,
  V90 = 25, V100 = 28, V110 = 30,
  V120 = 33, V130 = 36, V140 = 39,
  V150 = 42, V160 = 44
 }

 /// <summary>
 /// Stav nacitani vlaku.
 /// </summary>
 public enum StavNacitani
 {
  /// <summary>
  /// Nacten.
  /// </summary>
  OK,
  /// <summary>
  /// Neshoda s regularnim vyrazem.
  /// </summary>
  Neshoda,
  /// <summary>
  /// Nenacten (neznamy duvod).
  /// </summary>
  Nenacten,
  /// <summary>
  /// Vadny cas vstupu.
  /// </summary>
  CasVstupu,
  /// <summary>
  /// Vadny druh/kategorie vlaku.
  /// </summary>
  DruhVlaku,
  /// <summary>
  /// Vadne omezeni jizdy.
  /// </summary>
  OmezeniJizdy,
  /// <summary>
  /// Vadna rychlost.
  /// </summary>
  Rychlost,
  /// <summary>
  /// Vadna mimoradnost.
  /// </summary>
  Mimoradnost,
  /// <summary>
  /// Vadny obrat loko.
  /// </summary>
  ObratLoko,
  /// <summary>
  /// Vadny obrat soupravy.
  /// </summary>
  ObratSoupravy,
  /// <summary>
  /// Vadny jizdni rad.
  /// </summary>
  JizdniRad,
  /// <summary>
  /// Chybejici cilova stanice.
  /// </summary>
  StaniceCil,
  /// <summary>
  /// Spatna konfigurace stridani na ose.
  /// </summary>
  Osa,
  /// <summary>
  /// V oblasti neni dovolen odstup vlaku na posun.
  /// </summary>
  Posun
 }

 /// <summary>
 /// Chyby kontroly dat GVD.
 /// </summary>
 public enum ChybaKontrolyDat
 {
  /// <summary>
  /// Chybne definovane trasovani vlaku.
  /// </summary>
  Trasovani,
  /// <summary>
  /// Spatny jizdni rad vlaku Z-Do.
  /// </summary>
  JrZDo,
  /// <summary>
  /// Spatny jizdni rad vlaku Z-Pres-Do.
  /// </summary>
  JrZPresDo,
  /// <summary>
  /// Spatny jizdni rad vlaku trasovaneho zevnitr oblasti.
  /// </summary>
  JrZvnitrku,
  /// <summary>
  /// Spatny jizdni rad obratoveho vlaku zevnitr oblasti.
  /// </summary>
  JrObratZvnitrku
 }

 /// <summary>
 /// Rozliseni trasovani vlaku.
 /// </summary>
 public enum TrasovaniVlaku
 {
  /// <summary>
  /// Vlak bez "operacni" dopravny.
  /// </summary>
  Z_Do,
  /// <summary>
  /// Vlak s "operacni" dopravnou.
  /// </summary>
  Z_Pres_Do,
  /// <summary>
  /// Vlak zacinajici v oblasti (mel by prechazet z nejakeho vlaku).
  /// </summary>
  Zvnitrku,
  /// <summary>
  /// Vlak zacinajici v oblasti a ma obrat (= vyplna stanice "oper"),
  /// mel by prechazet z nejakeho vlaku.
  /// </summary>
  ZvnitrkuSObtatem,
  /// <summary>
  /// Neurcene trasovani vlaku.
  /// </summary>
  Nezname
 }

 /// <summary>
 /// Trida, reprezentujici vlak v grafikonu.
 /// </summary>
 public class Vlak
 {
  /// <summary>
  /// Syrova data.
  /// </summary>
  public string raw = null;
  /// <summary>
  /// Cislo vlaku na vstupu do oblasti.
  /// </summary>
  public uint cisloVlaku = 0;
  /// <summary>
  /// Cislo vlaku, na ktery nastoupi lokomotiva (tzv. obrat loko).
  /// </summary>
  public uint cisloObrLoko = 0;
  /// <summary>
  /// Cislo vlaku, na ktery prejde souprava (tzv. obrat soupravy).
  /// </summary>
  public uint cisloObrSoup = 0;
  /// <summary>
  /// Hodinovy cas vstupu (u nahodneho vlaku 99).
  /// </summary>
  public byte casHod = 0;
  /// <summary>
  /// Minutovy cas vstupu (u nahodneho vlaku 99).
  /// </summary>
  public byte casMin = 0;
  /// <summary>
  /// Delka vlaku v desitkach metru.
  /// </summary>
  public byte delkaVlaku = 0;
  /// <summary>
  /// Kategorie vlaku, default Pn.
  /// </summary>
  public KategorieVlaku kategorie = KategorieVlaku.Pn;
  /// <summary>
  /// Druh vlaku pro vypocet jizdnich dob.
  /// </summary>
  public VlDruhJD druhJD;
  /// <summary>
  /// Vychozi dopravna.
  /// </summary>
  public Dopravna dopravnaVych = null;
  /// <summary>
  /// "Operacni" stanice (uvrat, preprah, precislovani, osa).
  /// </summary>
  public Dopravna dopravnaOper = null;
  /// <summary>
  /// Cilova dopravna.
  /// </summary>
  public Dopravna dopravnaCil = null;
  /// <summary>
  /// Dopravna (depo), kam odstupuje Lv.
  /// </summary>
  public Dopravna dopravnaObratLv = null;
  /// <summary>
  /// Cilova dopravna vlaku trasovaneho zevnitr.
  /// </summary>
  public Dopravna dopravnaZvnitr = null;
  /// <summary>
  /// Omezeni jizdy vlaku (vychozi je Denne).
  /// </summary>
  public OmezeniJizdy omezeni = OmezeniJizdy.Denne;
  /// <summary>
  /// Mimoradnost u vlaku (postrk, ntp, mimoza, pendolino).
  /// </summary>
  public MimoradnostVlaku mimoradnost = MimoradnostVlaku.Zadna;
  /// <summary>
  /// Dopravna pro mimoradnost.
  /// </summary>
  public Dopravna dopravnaMimoradnost = null;
  /// <summary>
  /// Rychlost vlaku.
  /// </summary>
  public Rychlost rychlost;
  /// <summary>
  /// Smer jizdy vlaku (vychozi je Nedefinovany).
  /// </summary>
  public SmerJizdy smerJizdy = SmerJizdy.Nedefinovany;
  /// <summary>
  /// Novy smer jizdy vlaku (vychozi je Nedefinovany).
  /// </summary>
  public SmerJizdy smerNovy = SmerJizdy.Nedefinovany;
  /// <summary>
  /// Nutnost jizdy k nastupisti.
  /// </summary>
  public bool nastupiste = false;
  /// <summary>
  /// Posun v cilove stanici.
  /// </summary>
  public bool vlakNaPosun = false;
  /// <summary>
  /// Loko prechazi do volneho obehu (OPER.).
  /// </summary>
  public bool lokoDoObehu = false;
  /// <summary>
  /// Stridani na ose.
  /// </summary>
  public bool osa = false;
  /// <summary>
  /// Nahodny vstup do oblasti.
  /// </summary>
  public bool nahodnyVstup = false;
  /// <summary>
  /// Jizdni rad vlaku.
  /// </summary>
  public List<KrokJR> jizdniRad;
  /// <summary>
  /// Predchozi vlak (vyplneno pro vlaky trasovane zevnitr oblasti).
  /// </summary>
  public Vlak odVlaku = null;
  /// <summary>
  /// Nasledujici vlak (vyplneno pro vlaky s ???).
  /// </summary>
  public Vlak naVlak = null;
  /// <summary>
  /// Trasovani vlaku.
  /// </summary>
  public TrasovaniVlaku trasovani = TrasovaniVlaku.Nezname;

  /// <summary>
  /// Konstruktor.
  /// </summary>
  public Vlak()
  {
   jizdniRad = new List<KrokJR>();
  }

  /// <summary>
  /// Vrati cas odjezdu jako HH:MM (24-hodinovy format).
  /// </summary>
  public string CasOdjezdu
  {
   get {
    return String.Format("{0:d2}:{1:d2}", casHod, casMin);
   }
  }

  /// <summary>
  /// Vrati vychozi dopravnu nebo prazdny retezec, pokud neni.
  /// </summary>
  public string DopravnaVych
  {
   get
   {
    if (dopravnaVych == null)
     return "";
    else
     return dopravnaVych.nazev;
   }
  }

  /// <summary>
  /// Vrati "operacni" dopravnu nebo prazdny retezec, pokud neni.
  /// </summary>
  public string DopravnaOper
  {
   get
   {
    if (dopravnaOper == null)
     return "";
    else
     return dopravnaOper.nazev;
   }
  }

  /// <summary>
  /// Vrati cilovou dopravnu.
  /// </summary>
  public string DopravnaCil
  {
   get
   {
    if (dopravnaCil == null)
     return "";
    else
     return dopravnaCil.nazev;
   }
  }

  /// <summary>
  /// Naplni objekt Vlak z radku dat GVD.
  /// UCELEM JE PROVADET SYNTAKTICKOU KONTROLU!!!
  /// </summary>
  /// <param name="radekVlaku">Jeden radek dat GVD.</param>
  /// <param name="oblast">Oblast, pro kterou je vlak nacitan.</param>
  /// <returns>Vyctovou hodnotu StavNacitani, OK pokud je vlak nacten.</returns>
  public StavNacitani vlakFromString(string radekVlaku, Oblast oblast)
  {
   raw = radekVlaku;
   Match match = Utils.vlakRE.Match(raw);
   Group g;
   string s;
   char kod;
   StavNacitani r = StavNacitani.Neshoda;
   if (match.Groups.Count > 1)
   {
    r = StavNacitani.Nenacten;
    // cislo vlaku
    g = match.Groups[1];
    cisloVlaku = Utils.strToCisloOr0(g.Value);
    // cas odjezdu - hodiny
    g = match.Groups[2];
    casHod = Convert.ToByte(g.Value);
    // cas odjezdu - minuty
    g = match.Groups[3];
    casMin = Convert.ToByte(g.Value);
    // kontrola casu
    if (Utils.casNah(casHod, casMin))
     nahodnyVstup = true;
    if (!nahodnyVstup && !Utils.hodminOK(casHod, casMin))
     return StavNacitani.CasVstupu;
    // kategorie vlaku
    g = match.Groups[4];
    s = g.Value.Trim();
    if (!Utils.vlakDruh(s, out kategorie, out druhJD))
     return StavNacitani.DruhVlaku;
    // kod vychozi stanice
    g = match.Groups[5];
    kod = Utils.wc2z(g.Value[0]);
    dopravnaVych = Utils.dopravnaPodleKodu(oblast, kod);
    // kod stanice zmeny
    g = match.Groups[6];
    kod = Utils.wc2z(g.Value[0]);
    dopravnaOper = Utils.dopravnaPodleKodu(oblast, kod);
    // kod stanice cil
    g = match.Groups[7];
    kod = Utils.wc2z(g.Value[0]);
    dopravnaCil = Utils.dopravnaPodleKodu(oblast, kod);
    // kontrola dopravny
    if (dopravnaCil == null)
     return StavNacitani.StaniceCil;
    // omezeni jizdy
    g = match.Groups[8];
    kod = Utils.wc2z(g.Value[0]);
    if (!Utils.omezeniJizdy(kod, out omezeni))
     return StavNacitani.OmezeniJizdy;
    // rychlost
    g = match.Groups[9];
    if (!Utils.rychlost(g.Value, out rychlost))
     return StavNacitani.Rychlost;
    // mimoradnost
    g = match.Groups[10];
    kod = Utils.wc2z(g.Value[0]);
    if (!Utils.mimoradnost(kod, oblast, out mimoradnost, out dopravnaMimoradnost))
     return StavNacitani.Mimoradnost;
    // smer
    g = match.Groups[11];
    smerJizdy = Utils.smerJizdy(g.Value[0]);
    // delka vlaku
    g = match.Groups[12];
    delkaVlaku = Convert.ToByte(g.Value);
    // nastupiste
    g = match.Groups[13];
    nastupiste = Utils.TF(g.Value[0]);
    // cislo vlaku na ktery nastoupi loko
    g = match.Groups[14];
    s = g.Value;
    s = s.Trim();
    if (s == "")
    {
     cisloObrLoko = 0;
    }
    else
    {
     if (s.Length == 1)
     {
      // odstup loko do obvodu
      dopravnaObratLv = Utils.dopravnaPodleKodu(oblast, s[0]);
     }
     else if (s == "OPER.")
     {
      lokoDoObehu = true;
     }
     else
     {
      cisloObrLoko = Utils.strToCisloOr0(s);
      if (cisloObrLoko == 0)
       return StavNacitani.ObratLoko;
     }
    }
    // kod cil. stan Lv zvnitrku
    g = match.Groups[15];
    kod = Utils.wc2z(g.Value[0]);
    dopravnaZvnitr = Utils.dopravnaPodleKodu(oblast, kod);
    // novy smer vlaku
    g = match.Groups[16];
    kod = Utils.wc2z(g.Value[0]);
    smerNovy = Utils.smerJizdy(kod);
    // stridani na ose
    g = match.Groups[17];
    osa = g.Value[0] == 'o';
    // nove cislo vlaku
    g = match.Groups[18];
    s = g.Value;
    s = s.Trim();
    if (s == "")
    {
     cisloObrSoup = 0;
    }
    else if (s == "POSUN")
    {
     if (!oblast.posunVOblasti)
      return StavNacitani.Posun;
     cisloObrSoup = 0;
     vlakNaPosun = true;
    }
    else
    {
     cisloObrSoup = Utils.strToCisloOr0(s);
     if (cisloObrSoup == 0)
      return StavNacitani.ObratSoupravy;
    }
    // kontrola stridani na ose
    if (osa && (smerNovy == SmerJizdy.Nedefinovany || cisloObrSoup == 0))
     return StavNacitani.Osa;

    // jizdni rad
    g = match.Groups[19];
    s = g.Value;
    jizdniRad.Clear();
    KrokJR krokAkt = null;
    while (s != "")
    {
     krokAkt = new KrokJR();
     if (!krokAkt.fromString(ref s, oblast))
      return StavNacitani.JizdniRad;
     jizdniRad.Add(krokAkt);
    }

    r = StavNacitani.OK;
   }
   return r;
  } // public static Vlak vlakFromString(string radekVlaku)

  /// <summary>
  /// Funkce sestavi strucny popis udaju o vlaku.
  /// </summary>
  /// <returns>Strucny popis vlaku.</returns>
  public string souhrnVlaku()
  {
   string souhrn = "";
   // druh cislo
   souhrn = String.Format("{0} {1} {2}", kategorie, cisloVlaku, trasovani);
   //if(trasovani == TrasovaniVlaku.
   // souhrn += String.Format("{0} - {1}", dopravnaVych.nazev, dopravnaCil.nazev);
   return souhrn;
  }

  //
  //TODO: public static string vlakToString(Vlak v);
 }
}
